export {}
require('json5/lib/register')
const gulp = require('gulp')
const JSON5 = require('json5')
const fs = require('fs')
const del = require('rimraf')
const _ = require('lodash')
const log = require('fancy-log')
const Path = require('path')
const mkdirp = require('mkdirp')
const glob = require('glob')
const through = require('through2')
const newer = require('gulp-newer')

let {toPath,getFullPath, runCmdDetached, runCmd, delBuildDirAndFile} = require('sulp_utils')

let {buildTarget,compileIssueFac} = require('sulp_utils')

import {SyscCompileInterface} from "../protocol/syscCompileInterface"

let flow:SyscCompileInterface
let scProfile = null
let simProfile = null
let compileIssue=null

const updateSimSCLibs = ()=> {
  const buildDir = flow.scBuildDir
  const list=[]
  for(const sc_lib of flow.scLibList) {
    const libName = Path.basename(sc_lib)
    list.push(toPath(scProfile.scTargetDir(),libName+'.so'))
  }
  simProfile.elabPlan.elabArgs.setSCLibs(list);
}

const buildScObj = (cppList:string[],dst:string) => {
  const pList=[]
  const targetList=[]
  for(const file of cppList) {
    const p=new Promise((resolve,reject)=> {
      const ret=scProfile.compileSC({
        file: file
        })
      targetList.push(ret.target)
      buildTarget(ret.cmd,ret.args,ret.target).then(()=>{
        resolve()
      })
    })
    pList.push(p)
  }
  return Promise.all(pList).then(()=>{
    const ret=scProfile.linkSC({
      target: dst,
      obj_list: targetList,
      static_lib_list: [],
    })
    buildTarget(ret.cmd,ret.args,ret.target)
  })
}

gulp.task('sysc_model:compile',()=>{
  const cache={}
  const libs=[]
  const pList=[]
  for(const sc_lib of flow.scLibList) {
    const libName = Path.basename(sc_lib)
    const srcList=glob.sync(sc_lib+"/*.cpp")
    for(const file of srcList) {
      const target= scProfile.scTarget({
            lib_name: libName
          })
      if (flow.force) {
        del.sync(target + '.so');
      }
      cache[Path.basename(file)]={
        dst: target,
        files:srcList,
      }
    }
    libs.push(srcList)
  }
  const busy={}
  for(const flist of libs) {
    const p=new Promise((resolve)=>{
      gulp.src(flist)
      .pipe(newer({
        map:(path)=> {
          return cache[path].dst+'.so'
        }})
      )
      .pipe(through.obj((chunk, enc, cb) => {
        const key=Path.basename(chunk.path)
        const dst=cache[key].dst
        const files=cache[key].files
        if(busy[dst]) {
          cb(null)
          return
        }
        else {
          busy[dst]=1
        }
        const cppFiles=_.filter(files,(i)=>i.match(/\.cpp$/))
        compileIssue(cppFiles,dst).then(()=> {
          cb(null)
        }).catch((e)=> {
          log('Error:'+e)
        })
      }))
      .pipe(gulp.dest(scProfile.scTargetDir()))
      .on('end',()=>{
        resolve()
      })
    })
    pList.push(p)
  }
  updateSimSCLibs()
  return Promise.all(pList)
})

module.exports.init = function(program) {
  program.allowUnknownOption()
    .option('--scModelListFile <file>', 'systemc model list file')
    .option('--scBuildDir <dir>', 'systemc object build directory')
    .option('--scUseCpuNum <num>', 'specify the cpu nubmers')
    .option('--force', 'force recompile the model')
}

module.exports.flowName='sysc_model'

module.exports.setEnv = function(e) {
  let env = e

  flow = <SyscCompileInterface>env.getFlow('sysc_model')
  scProfile=env.useProfile('sysc_model')
  simProfile=env.useProfile('sim')

  compileIssue= compileIssueFac(env.getOpt('sysc_model.scUseCpuNum',8),buildScObj)

  flow.scBenchRoot= env.getOpt('sysc_model.scBenchRoot',process.env.SC_BENCH_ROOT)

  const modelListFile= env.getOpt('sysc_model.scModelListFile',toPath(flow.scBenchRoot,'sc_model/model_list.ts'))
  const modelList=require(modelListFile)
  flow.force    = env.getOpt('sysc_model.force',false)
  flow.scLibList=[...modelList.C_MODEL_LIST , ...modelList.SC_MODEL_LIST]
  flow.vlogWrapperList=[]
  for(const i of modelList.SC_MODEL_LIST) {
    const dirName=Path.dirname(i)
    const baseName=Path.basename(i)
    const wrapperPath=toPath(dirName,'verilog_model',baseName+'.v')
    flow.vlogWrapperList.push(wrapperPath)
  }
  flow.scIncDirList=env.getOpt('sysc_model.scIncDirList',[toPath(flow.scBenchRoot,'sc_model/include')])
  flow.scBuildDir = env.getOpt('sysc_model.scBuildDir',toPath(env.flow.global.workDir,'BUILD'))
  if(!fs.existsSync(flow.scBuildDir)) {
    mkdirp.sync(flow.scBuildDir)
  }
  scProfile.setBuildDir(flow.scBuildDir)
  for(const i of flow.scIncDirList) {
    scProfile.addIncDir(i)
  }
  updateSimSCLibs()
}
